﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
/////////////////////////////////////
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using BingMapBrowser.Data;
using System.Collections.Generic;
using Microsoft.Maps.MapControl;

namespace BingMapBrowser.ViewModel
{
	public class EntityViewModel : ViewModelBase
	{
		[Import(typeof(IEntityDataService))]
		public IEntityDataService Service { get; set; }


		public EntityViewModel()
		{
			CompositionInitializer.SatisfyImports(this);

			Service.RecordsChanged += new Action<IEnumerable<MyEntity>>(Service_RecordsChanged);
		}

		void Service_RecordsChanged(IEnumerable<MyEntity> obj)
		{
			AllRecords = obj;
			OnPropertyChanged("AllRecords");
			GetNearRecords(0.0, true);
		}

		//ToDo: Eventually metadata will move here. Since we need to bind that too with filters.
		public void QueryEntity(MyEntity[] metadata)
		{
			Metadata = new ObservableCollection<MyEntity>(metadata);
			Service.QueryData(Metadata);
		}

		public void QueryEntity()
		{
			if (Metadata == null)
			{
				MessageBox.Show("Metadata of Entities is not set yet.");
				return;
			}
			Service.QueryData(Metadata);
		}

		public void GetNearRecords(double distance, bool isNearDistanceInMiles)
		{
			if (isNearDistanceInMiles)
			{
				distance = distance * 1.60934;
			}

			if (NearRecords == null)
			{
				NearRecords = new ObservableCollection<MyEntity>();
			}
			else
			{
				NearRecords.Clear();
			}
			try
			{
				foreach (var record in AllRecords)
				{
					//Put some condition based on location.
					if (ClickedLocation != null && distance != 0.0)
					{
						double actualDist = GetDistBetweenLocations(ClickedLocation, record.Address.Location);
						//MessageBox.Show("ActualDistance: " + actualDist.ToString());
						if (actualDist <= distance)
						{
							NearRecords.Add(record);
						}
					}
					else
					{
						NearRecords.Add(record);
					}
				}
				OnPropertyChanged("NearRecords");
			}
			catch (Exception e)
			{
				MessageBox.Show("Error occured while retrieving Near records. Exception: " + e.Message);
			}
		}

		private Double GetDistBetweenLocations(Location loc1, Location loc2)
		{
			try
			{
				double earthRadius = 6371;
				Location rloc1 = new Location(GetRadianValue(loc1.Latitude), GetRadianValue(loc1.Longitude));
				Location rloc2 = new Location(GetRadianValue(loc2.Latitude), GetRadianValue(loc2.Longitude));
				double dLat = (rloc2.Latitude - rloc1.Latitude);
				double dLong = (rloc2.Longitude - rloc1.Longitude);
				double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(rloc1.Latitude) * Math.Cos(rloc2.Latitude) * Math.Sin(dLong / 2) * Math.Sin(dLong / 2);
				double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
				double d = earthRadius * c;
				return d;
			}
			catch (Exception e)
			{
				MessageBox.Show("Exception occured while calculating distance between locations. Exception: " + e.Message);
				return 0.0;
			}
		}

		private double GetRadianValue(double deg)
		{
			return (deg * Math.PI) / 180;
		}

		#region Bindable Properties
		public ObservableCollection<MyEntity> Metadata { get; set; }
		public IEnumerable<MyEntity> AllRecords { get; set; }
		public ObservableCollection<MyEntity> NearRecords { get; set; }
		public Location ClickedLocation { get; set; }
		#endregion
	}
}
